package domain;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import center.BaseDomain;
import center.DateUtil;

public class Project extends BaseDomain{
	private long id;
	private String name;
	private Timestamp created_at;
	private String goal;
	
	private List<Criteria> criterias = new ArrayList<Criteria>();
	private List<Alternative> alternatives = new ArrayList<Alternative>();
	private List<CriteriaPair> criteriaPairs = new ArrayList<CriteriaPair>();
	private List<ProjectVector> projectVectors = new ArrayList<ProjectVector>();
	private Project project;
	private List<Project> replica = new ArrayList<Project>();

	public List<Project> getReplica() {
		return replica;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public Timestamp getCreated_at() {
		return created_at;
	}
	public void setCreated_at(Timestamp created_at) {
		this.created_at = created_at;
	}
	public String getGoal() {
		return goal;
	}
	public void setGoal(String goal) {
		this.goal = goal;
	}
	public List<Criteria> getCriterias() {
		return criterias;
	}
	public void setCriterias(List<Criteria> criterias) {
		this.criterias = criterias;
	}
	public List<Alternative> getAlternatives() {
		return alternatives;
	}
	public void setAlternatives(List<Alternative> alternatives) {
		this.alternatives = alternatives;
	}
	public List<CriteriaPair> getCriteriaPairs() {
		return criteriaPairs;
	}
	public List<ProjectVector> getProjectVectors() {
		return projectVectors;
	}
	public Project getProject() {
		return project;
	}
	public void setProject(Project project) {
		this.project = project;
	}
	
	//Business Logic and Transaction
	public void insert() throws SQLException{
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("insert into projects(name, goal, created_at, project_id) ");
			 sql.append(" values(?, ?, ?, ?)");
		 int index =1;
		 ps = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
		 	ps.setString(index++, this.name);
		 	ps.setString(index++, this.goal);
		 	ps.setTimestamp(index++, this.created_at);
		 	if(this.project!=null){
		 		ps.setLong(index++, this.project.getId());
		 	}else{
		 		ps.setLong(index++, 0);
		 	}
		 ps.executeUpdate();
		 
		 ResultSet resultSet = ps.getGeneratedKeys();
			if (resultSet.next()) {
				long generatedid = resultSet.getLong(1);
				ps.close();
				conn.close();
				this.id = generatedid;
			} else {
				 System.out.println("Cannot Get Genereated ID!!!!");
			}
	}
	
	public static List<Project> getAllProjects() throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, goal, created_at, project_id from projects ");
		 ps = conn.prepareStatement(sql.toString());
		 	
		 ResultSet rs = ps.executeQuery();
		 
		 List<Project> projects = new ArrayList<Project>();
		 while(rs.next()){
			 Project project = new Project();
			 	project.setName(rs.getString("name"));
			 	project.setId(rs.getLong("id"));
			 	project.setGoal(rs.getString("goal"));
			 	project.setCreated_at(rs.getTimestamp("created_at"));
			 	if(rs.getLong("project_id")!=0)
			 	project.setProject(Project.find(rs.getLong("project_id")));
			 projects.add(project);
		 }
		 ps.close();
		 conn.close();
		 
		 return projects;
	}
	
	public static Project find(long id) throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, goal, created_at, project_id from projects where id = ? ");
		int index=1;
		 ps = conn.prepareStatement(sql.toString());
		 	ps.setLong(index++, id);
		 ResultSet rs = ps.executeQuery();
		 
		 Project project = new Project();
		 while(rs.next()){
			 	project.setName(rs.getString("name"));
			 	project.setId(rs.getLong("id"));
			 	project.setGoal(rs.getString("goal"));
			 	project.setCreated_at(rs.getTimestamp("created_at"));
			 	if(rs.getLong("project_id")!=0)
			 	project.setProject(Project.find(rs.getLong("project_id")));
		 }
		 ps.close();
		 conn.close();
		 
		 return project;
	}
	
	public void findCriterias() throws SQLException {
		List<Criteria> criteriaList = Criteria.getCriteriasOf(this.id);
		this.criterias = criteriaList;
	}
	
	public void newCriteria(Criteria criteria) throws SQLException{
		criteria.setProject(this);
		criteria.insert();
	}
	
	public void findAlternatives() throws SQLException {
		List<Alternative> alterList = Alternative.getAlternativesOf(this.id);
		this.alternatives = alterList;
	}
	
	public void newAlternative(Alternative alternative) throws SQLException{
		alternative.setProject(this);
		alternative.insert();
	}
	
	public void findComparison() throws SQLException {
		this.criteriaPairs = ProjectPair.findByProjectId(this.id);
	}
	
	//need to do findCriteria() and findComparison() before executing
	public void createVector() throws SQLException {
		List<Criteria> subCriteria = findImmediateCriteria();
		double [][] metrix = new double[subCriteria.size()+1][subCriteria.size()+1];
		
		ArrayList<Long> criteriaIds = new ArrayList<Long>();
		
		for(int i=0; i<this.criteriaPairs.size(); i++){
			CriteriaPair cp = criteriaPairs.get(i);
			if(i==0){
				criteriaIds.add(cp.getSubject().getId());
				criteriaIds.add(cp.getObject().getId());
			}else{
				if(!criteriaIds.contains(cp.getSubject().getId())){
					criteriaIds.add(cp.getSubject().getId());
				}
				
				if(!criteriaIds.contains(cp.getObject().getId())){
					criteriaIds.add(cp.getObject().getId());
				}
			}
		}
		
		for(int i=0; i<criteriaIds.size(); i++){
			metrix[i+1][0] = criteriaIds.get(i);
			metrix[0][i+1] = criteriaIds.get(i);
			metrix[i+1][i+1] = 1.0;
		}
		
		for(int i=0; i<this.criteriaPairs.size(); i++){
			CriteriaPair cp = this.criteriaPairs.get(i);
			for(int row=0; row<subCriteria.size(); row++){
				if(metrix[row+1][0]==cp.getSubject().getId()){
					for(int col=0; col<subCriteria.size(); col++){
						if(metrix[0][col+1]==cp.getObject().getId()){
							metrix[row+1][col+1] = cp.getScore();
							metrix[col+1][row+1] = 1.0/cp.getScore();
						}
					}
				}
			}
			
		}
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [] summary = new double [subCriteria.size()+1];
		for(int row=0; row<subCriteria.size()+1; row++){
				summary[row] = 0.0;
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				summary[col+1] += metrix[row+1][col+1];
			}
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				metrix[row+1][col+1] = metrix[row+1][col+1]/summary[col+1];
			}
		}
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [][] priority = new double [subCriteria.size()][2];
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = 0.0;
		}
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][0] = metrix[row+1][0];
			for(int col=0; col<subCriteria.size(); col++){
				priority[row][1] += metrix[row+1][col+1];
			}
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = priority[row][1]/subCriteria.size();
		}
		
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size(); row++){
			for(int col=0; col<2; col++){
				System.out.print(priority[row][col]+"\t");
			}
			System.out.println("");
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			ProjectVector pv = new ProjectVector();
				pv.setType("project");
				pv.setPriority(priority[row][1]);
				pv.setProject(this);
				Criteria c = Criteria.find((long) priority[row][0]);
				c.setPriority(priority[row][1]);
				pv.setCriteria(c);
				
				pv.insert();
				c.update();
		}
	}
	
	public List<Criteria> findImmediateCriteria() {
		List<Criteria> criteriaList = new ArrayList<Criteria>();
		for(int i=0; i<this.criterias.size(); i++){
			Criteria c = criterias.get(i);
			if(c.getSubOf()==null){
				criteriaList.add(c);
			}
		}
		return criteriaList;
	}
	
	public void calculateResult() throws SQLException{
		List<Criteria> immediateChild = findImmediateCriteria();
		for(int a=0; a<this.getAlternatives().size(); a++){
			double result=0.0;
			for(int i=0; i<immediateChild.size(); i++){
				Criteria c = immediateChild.get(i);
				List<Criteria> subCriterias = c.getSubCriterias();
				if(subCriterias.size()!=0){
					for(int j=0; j<this.getAlternatives().size(); j++){
						double sum=0.0;
						for(int k=0; k<subCriterias.size(); k++){
							Criteria subCriteria = subCriterias.get(k);
							subCriteria.findAlternativeVector();
							for(int z=0; z<subCriteria.getAlternativeVectors().size(); z++){
								AlternativeVector av = subCriteria.getAlternativeVectors().get(z);
								if(av.getAlternative().getId() == this.getAlternatives().get(j).getId()){
									sum += av.getPriority()*subCriteria.getPriority();
								}
							}
						}
						sum = sum*c.getPriority();
						
//						System.out.println("Criteria "+c.getName()+" : SUM OF "+this.getAlternatives().get(j).getName()+" = "+sum);
						if(this.getAlternatives().get(a).getId() == this.getAlternatives().get(j).getId()){
							result += sum;
						}
					}
					
				}else{
					c.findAlternativeVector();
					for(int z=0; z<c.getAlternativeVectors().size(); z++){
						AlternativeVector av = c.getAlternativeVectors().get(z);
						if(this.getAlternatives().get(a).getId() == av.getAlternative().getId()){
							result += av.getPriority()*c.getPriority();
						}
					}
				}
			}
			this.getAlternatives().get(a).setPriority(result);
			System.out.println("Alter: "+this.getAlternatives().get(a).getName()+" = "+ result);
			this.getAlternatives().get(a).update();
		}
	}
	
	public void duplicate() throws SQLException{
		this.project = this;
		this.created_at = DateUtil.getCurrentTimestamp();
		this.name = this.name + " (Duplication)";
		this.insert();
		for(int i=0; i<this.getAlternatives().size(); i++){
			Alternative alternative = this.getAlternatives().get(i);
				alternative.setProject(this);
				alternative.insert();
		}
		
		for(int i=0; i<this.getCriterias().size(); i++){
			Criteria criteria = this.getCriterias().get(i);
				criteria.setProject(this);
				criteria.insert();
		}
	}
	
	public boolean isDuplication(){
		boolean isDup = false;
		if(this.project!=null){
			isDup = true;
		}
		return isDup;
	}
	
	public void findReplica() throws SQLException{
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, goal, created_at, project_id from projects where project_id = ? ");
		 ps = conn.prepareStatement(sql.toString());
		 	int index=1;
		 	ps.setLong(index++, this.id);
		 ResultSet rs = ps.executeQuery();
		 
		 List<Project> projects = new ArrayList<Project>();
		 while(rs.next()){
			 Project project = new Project();
			 	project.setName(rs.getString("name"));
			 	project.setId(rs.getLong("id"));
			 	project.setGoal(rs.getString("goal"));
			 	project.setCreated_at(rs.getTimestamp("created_at"));
			 	if(rs.getLong("project_id")!=0)
			 	project.setProject(Project.find(rs.getLong("project_id")));
			 projects.add(project);
		 }
		 ps.close();
		 conn.close();
		 
		 this.replica = projects;
	}
	
	public List<Alternative> findCombinationResult() throws SQLException{
		List<Alternative> alters = new ArrayList<Alternative>();
			for(int a=0; a<this.alternatives.size(); a++){
				Alternative result = new Alternative();
				Alternative alter = this.alternatives.get(a);
				result.setName(alter.getName());
				double priority = alter.getPriority();
				for(int i=0; i<this.replica.size(); i++){
					Project p = this.replica.get(i);
					p.findAlternatives();
					for(int j=0; j<p.getAlternatives().size(); j++){
						if(p.getAlternatives().get(j).getName().equals(alter.getName())){
							priority += p.getAlternatives().get(j).getPriority();
							break;
						}
					}
					
				}
				double noOfProject = this.replica.size()+1;
				result.setPriority(priority/noOfProject);
				alters.add(result);
			}
		return alters;
	}
	
}
